package com.bestcem.xm.common.core.uitls;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class ExcelExportUtil {

    public static final String ROOT_FILE_PATH = System.getProperty("user.dir");
    public static final String TEMP_PATH = ROOT_FILE_PATH + File.separator + "temp" + File.separator;
    public static final String EXCEL_CONTENT_TYPE = "application/vnd.ms-excel";
    public static final String UTF_8 = "UTF-8";
    public static final String CONTENT_DISPOSITION = "Content-Disposition";
    public static final String ATTACHMENT_FILENAME = "attachment;filename=";
    public static final String XLSX_SUFFIX = ".xlsx";
    public static final String SHEET_NAME = "sheet";
    public static final Integer DEFAULT_CELL_LENGTH = 20;

    /**
     * 动态生成导出模版(单表头)
     *
     * @param headColumns 列名称
     * @return excel文件流
     */
    public static byte[] exportTemplateExcelFile(List<String> headColumns) {
        List<List<String>> excelHead = Lists.newArrayList();
        headColumns.forEach(columnName -> excelHead.add(Lists.newArrayList(columnName)));
        return createExcelFile(excelHead, new ArrayList<>());
    }

    /**
     * 动态生成模版(复杂表头)
     *
     * @param excelHead 列名称
     * @return excel文件流
     */
    public static byte[] exportTemplateExcelFileCustomHead(List<List<String>> excelHead) {
        return createExcelFile(excelHead, new ArrayList<>());
    }

    /**
     * 动态导出文件（通过map方式计算）
     *
     * @param headColumnMap 有序列头部
     * @param dataList      数据体
     * @return excel文件流
     */
    public static byte[] exportExcelFile(LinkedHashMap<String, String> headColumnMap, List<Map<String, Object>> dataList) {
        //获取列名称
        List<List<String>> excelHead = new ArrayList<>();
        if (MapUtils.isNotEmpty(headColumnMap)) {
            //key为匹配符，value为列名，如果多级列名用逗号隔开
            headColumnMap.forEach((key, value) -> excelHead.add(Lists.newArrayList(value.split(","))));
        }
        List<List<Object>> excelRows = new ArrayList<>();
        if (MapUtils.isNotEmpty(headColumnMap) && CollectionUtils.isNotEmpty(dataList)) {
            for (Map<String, Object> dataMap : dataList) {
                List<Object> rows = new ArrayList<>();
                headColumnMap.forEach((key, value) -> {
                    if (dataMap.containsKey(key)) {
                        Object data = dataMap.get(key);
                        rows.add(data);
                    }
                });
                excelRows.add(rows);
            }
        }
        return createExcelFile(excelHead, excelRows);
    }


    /**
     * 生成文件（自定义头部排列）
     *
     * @param rowHeads  表头
     * @param excelRows 数据行
     * @return excel文件流
     */
    public static byte[] customerExportExcelFile(List<List<String>> rowHeads, List<List<Object>> excelRows) {
        //将行头部转成easyexcel能识别的部分
        List<List<String>> excelHead = transferHead(rowHeads);
        return createExcelFile(excelHead, excelRows);
    }

    /**
     * 生成文件
     *
     * @param excelHead 表头
     * @param excelRows 数据行
     * @return excel文件流
     */
    private static byte[] createExcelFile(List<List<String>> excelHead, List<List<Object>> excelRows) {
        try {
            if (CollectionUtils.isNotEmpty(excelHead)) {
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                EasyExcel.write(outputStream).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                        .head(excelHead)
                        .sheet(SHEET_NAME)
                        .doWrite(excelRows);
                return outputStream.toByteArray();
            }
        } catch (Exception e) {
            log.error("动态生成excel文件失败，headColumns：" + JSONArray.toJSONString(excelHead) + "，excelRows：" + JSONArray.toJSONString(excelRows), e);
        }
        return null;
    }

    /**
     * 将行头部转成easyexcel能识别的部分
     *
     * @param rowHeads 表头
     * @return 可识别的表头
     */
    public static List<List<String>> transferHead(List<List<String>> rowHeads) {
        //将头部列进行反转
        List<List<String>> realHead = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(rowHeads)) {
            Map<Integer, List<String>> cellMap = new LinkedHashMap<>();
            //遍历行
            for (List<String> cells : rowHeads) {
                //遍历列
                for (int i = 0; i < cells.size(); i++) {
                    if (cellMap.containsKey(i)) {
                        cellMap.get(i).add(cells.get(i));
                    } else {
                        cellMap.put(i, Lists.newArrayList(cells.get(i)));
                    }
                }
            }
            //将列一行一行加入realHead
            cellMap.forEach((key, value) -> realHead.add(value));
        }
        return realHead;
    }

    /**
     * 导出生成excel数据流
     *
     * @param list  导出数据list
     * @param clazz DTO.class
     * @param <T>   导出数据
     * @return excel数据byte流
     */
    public static <T> byte[] createExcelStream(List<T> list, Class<T> clazz) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
             ExcelWriter writer = EasyExcel.write(outputStream, clazz).build()) {
            WriteSheet sheet = EasyExcel.writerSheet(SHEET_NAME).build();
            writer.write(list, sheet);
            writer.finish();
            return outputStream.toByteArray();
        } catch (Exception e) {
            log.error("导出生成excel文件流失败");
        }
        return null;
    }


    /**
     * 导出生成Excel file
     *
     * @param list     导出数据list
     * @param clazz    DTO.class
     * @param fileName 文件名
     * @param <T>      DTO
     * @return file
     */
    public static <T> File createExcelStream(List<?> list, Class<T> clazz, String fileName) {
        File file = new File(fileName);
        try (ExcelWriter writer = EasyExcel.write(file, clazz).build()) {
            WriteSheet sheet = EasyExcel.writerSheet(SHEET_NAME).build();
            writer.write(list, sheet);
            writer.finish();
            return file;
        } catch (Exception e) {
            log.error("导出生成excel文件失败");
        }
        return null;
    }

    /**
     * 多shhet，多实体导出文件流
     * 需要定义导出实体class，使用EasyExcel注解配置表头
     *
     * @param head       表头
     * @param exportData 数据
     * @param sheetNames sheet名称列表
     * @return byte[]
     * @author chenglong.yue <chenglong.yue@idiaoyan.com>
     * @date 2022/12/9 15:46
     */
    public static <T> byte[] createExportFileStream4Sheets(List<T> head, List<List<T>> exportData, List<String> sheetNames) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 设置导出的表格样式
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = getExportDefaultStyle();
        try (ExcelWriter writer = EasyExcel.write(outputStream).registerWriteHandler(horizontalCellStyleStrategy).build()) {
            for (int itemIndex = 0; itemIndex < exportData.size(); itemIndex++) {
                // 表头数据
                Object headData = head.get(itemIndex);
                // sheet页的数据
                List<T> list = exportData.get(itemIndex);
                WriteSheet sheet = EasyExcel.writerSheet(itemIndex, CollUtil.isEmpty(sheetNames) ? SHEET_NAME + itemIndex + 1 : sheetNames.get(itemIndex)).head(headData.getClass()).build();
                writer.write(list, sheet);
            }
            writer.finish();
            return outputStream.toByteArray();
        } catch (Exception e) {
            log.error("导出生成excel文件失败");
        }
        return null;


    }

    /**
     * excel文件导出，不带表头
     *
     * @param fileName   导出文件名
     * @param sheetName  sheetName
     * @param exportData 需要导出数据
     */
    public static File exportNoHeadFile(String fileName, List<List<String>> exportData, String sheetName) {
        if (StrUtil.isBlank(fileName)) {
            log.error("ExcelExportUtil exportFile required param can't be empty");
            return null;
        }
        File parentfile = new File(TEMP_PATH);
        if (!parentfile.exists()) {
            parentfile.mkdirs();
        }
        File file = new File(TEMP_PATH, StrUtil.addSuffixIfNot(fileName, XLSX_SUFFIX));
        // 设置导出的表格样式
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = getExportDefaultStyle();
        try (ExcelWriter writer = EasyExcel.write(file).registerWriteHandler(horizontalCellStyleStrategy).build()) {
            WriteSheet sheet = EasyExcel.writerSheet(0, sheetName).build();
            writer.write(exportData, sheet);
            writer.finish();
            return file;
        } catch (Exception e) {
            log.error("ExcelExportUtil exportNoHeadFile in error:{}", e.getMessage());
        }
        return null;
    }

    /**
     * 多shhet，动态表头导出文件流
     * 无需写实体Class
     *
     * @param head       表头
     * @param exportData 数据
     * @param sheetNames sheet名称列表
     * @return byte[]
     * @author chenglong.yue <chenglong.yue@idiaoyan.com>
     * @date 2022/12/9 15:46
     */
    public static <T> byte[] createDynamicExportFileStream4Sheets(List<List<List<String>>> head, List<List<List<T>>> exportData, List<String> sheetNames) {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 设置默认样式的excel表格对象
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = getExportDefaultStyle();
        AbstractColumnWidthStyleStrategy columnWidthStyleStrategy = new SimpleColumnWidthStyleStrategy(DEFAULT_CELL_LENGTH);
        try (ExcelWriter writer = EasyExcel.write(outputStream).registerWriteHandler(horizontalCellStyleStrategy).registerWriteHandler(columnWidthStyleStrategy).build()) {
            for (int i = 0; i < exportData.size(); i++) {
                List<List<T>> tableData = exportData.get(i);
                WriteSheet sheet = EasyExcel.writerSheet(i, CollectionUtil.isEmpty(sheetNames) ? SHEET_NAME + i + 1 : sheetNames.get(i)).head(head.get(i)).build();
                writer.write(tableData, sheet);
            }
            writer.finish();
            return outputStream.toByteArray();
        } catch (Exception e) {
            log.error("ExcelExportUtil createDynamicExportFileStream4Sheets in error:{}", e.getMessage());
        }
        return null;
    }

    /**
     * 导出execl到临时目录
     *
     * @param fileName
     * @param head
     * @param exportData
     * @param sheetNames
     * @return java.io.File
     * @author chenglong.yue <chenglong.yue@idiaoyan.com>
     * @date 2023/7/19 16:12
     */
    public static <T> File exportWithDynamicData2LocalDir(String fileName, List<List<List<String>>> head, List<List<List<T>>> exportData, List<String> sheetNames) {
        File parentfile = new File(TEMP_PATH);
        if (!parentfile.exists()) {
            parentfile.mkdirs();
        }
        File exportFile = new File(TEMP_PATH, StrUtil.addSuffixIfNot(fileName, XLSX_SUFFIX));
        byte[] stream = createDynamicExportFileStream4Sheets(head, exportData, sheetNames);

        try (FileOutputStream outputStream = new FileOutputStream(exportFile)) {
            Assert.notNull(stream, "stream is null");
            outputStream.write(stream);
            return exportFile;
        } catch (Exception e) {
            log.error("ExcelExportUtil exportWithDynamicData2LocalDir in error:{}", e.getMessage());
        }
        return null;
    }

    /**
     * 下载指定路径下的模板
     *
     * @param filePath 文件所在路径(包含模板名称)：一般是放在项目的resources目录下的template
     * @param fileName 下载时默认的文件名称
     * @param response 响应流
     */
    public static void downloadTemplate(String filePath, String fileName, String fileSuffix, HttpServletResponse response) {
        try {
            // 设置浏览器以附件形式读取响应流中的数据
            response.setContentType(EXCEL_CONTENT_TYPE);
            response.setCharacterEncoding(UTF_8);
            response.setHeader(CONTENT_DISPOSITION, ATTACHMENT_FILENAME + URLEncodeUtil.encode(fileName) + fileSuffix);
            byte[] bytes = ResourceUtil.readBytes(filePath);
            response.getOutputStream().write(bytes);
        } catch (IOException e) {
            log.error("ExcelExportUtil downloadTemplate in error:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 配置默认的excel表格样式对象
     *
     * @return com.alibaba.excel.write.style.HorizontalCellStyleStrategy
     * @date 2022/12/6 10:28
     */
    private static HorizontalCellStyleStrategy getExportDefaultStyle() {
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //设置头字体
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        //设置头居中
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //内容策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //设置 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }
}
